package hkust.cse.calendar.gui;

import hkust.cse.calendar.apptstorage.ApptStorageControllerImpl;
import hkust.cse.calendar.unit.Appt;
import hkust.cse.calendar.unit.TimeSpan;
import hkust.cse.calendar.unit.User;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import javax.swing.table.TableModel;
import javax.swing.text.BadLocationException;


public class AppScheduler extends JDialog implements ActionListener,
ComponentListener {

	private JLabel yearL;
	private JTextField yearF;
	private JLabel monthL;
	private JTextField monthF;
	private JLabel dayL;
	private JTextField dayF;
	private JLabel sTimeHL;
	private JTextField sTimeH;
	private JLabel sTimeML;
	private JTextField sTimeM;
	private JLabel eTimeHL;
	private JTextField eTimeH;
	private JLabel eTimeML;
	private JTextField eTimeM;
	private JLabel locationL;
	private JComboBox<String> location;
	private  Vector<String> locations = new Vector<String>();	// various sizes for the locations , can let the user defines the locations by itself





	private DefaultListModel model;
	private JTextField titleField;

	private JButton saveBut;
	private JButton CancelBut;
	private JButton inviteBut;
	private JButton rejectBut;

	private Appt NewAppt;
	private CalGrid parent;
	private boolean isNew = true;
	private boolean isChanged = true;
	private boolean isJoint = false;

	private JTextArea detailArea;

	private JSplitPane pDes;
	JPanel detailPanel;

	private JTextField attendField;
	private JTextField rejectField;
	private JTextField waitingField;
	private int selectedApptId = -1;


	private void commonConstructor(String title, CalGrid cal) {
		parent = cal;
		this.setAlwaysOnTop(true);
		setTitle(title);
		setModal(false);

		Container contentPane;
		contentPane = getContentPane();

		JPanel pDate = new JPanel();
		Border dateBorder = new TitledBorder(null, "DATE");
		pDate.setBorder(dateBorder);

		yearL = new JLabel("YEAR: ");
		pDate.add(yearL);
		yearF = new JTextField(6);
		pDate.add(yearF);
		monthL = new JLabel("MONTH: ");
		pDate.add(monthL);
		monthF = new JTextField(4);
		pDate.add(monthF);
		dayL = new JLabel("DAY: ");
		pDate.add(dayL);
		dayF = new JTextField(4);
		pDate.add(dayF);

		JPanel psTime = new JPanel();
		Border stimeBorder = new TitledBorder(null, "START TIME");
		psTime.setBorder(stimeBorder);
		sTimeHL = new JLabel("Hour");
		psTime.add(sTimeHL);
		sTimeH = new JTextField(4);
		psTime.add(sTimeH);
		sTimeML = new JLabel("Minute");
		psTime.add(sTimeML);
		sTimeM = new JTextField(4);
		psTime.add(sTimeM);

		JPanel peTime = new JPanel();
		Border etimeBorder = new TitledBorder(null, "END TIME");
		peTime.setBorder(etimeBorder);
		eTimeHL = new JLabel("Hour");
		peTime.add(eTimeHL);
		eTimeH = new JTextField(4);
		peTime.add(eTimeH);
		eTimeML = new JLabel("Minute");
		peTime.add(eTimeML);
		eTimeM = new JTextField(4);
		peTime.add(eTimeM);

		JPanel pTime = new JPanel();
		pTime.setLayout(new BorderLayout());
		pTime.add("West", psTime);
		pTime.add("East", peTime);

		JPanel top = new JPanel();
		top.setLayout(new BorderLayout());
		top.setBorder(new BevelBorder(BevelBorder.RAISED));
		top.add(pDate, BorderLayout.NORTH);
		top.add(pTime, BorderLayout.CENTER);

		contentPane.add("North", top);

		JPanel titleAndTextPanel = new JPanel(); // this panel contains both the title and location
		JLabel titleL = new JLabel("TITLE");
		titleAndTextPanel.add(titleL);
		titleField = new JTextField(15);
		titleAndTextPanel.add(titleField);

		locationL = new JLabel("Location ");
		titleAndTextPanel.add(locationL);		
		location = new JComboBox();
		location.addActionListener(this);
		location.setPreferredSize(new Dimension(200, 30));

		locations.add("HKUST");
		locations.add("HKU");
		locations.add("HKCU");
		locations.add("Tseung Kwan O");
		locations.add("Kwun Tong");
		locations.add("More...");


		for (int cnt = 0; cnt < locations.size(); cnt++)
			location.addItem(locations.get(cnt));
		titleAndTextPanel.add(location);

		//location.setSelectedIndex(temp - 1);

		detailPanel = new JPanel();
		detailPanel.setLayout(new BorderLayout());
		Border detailBorder = new TitledBorder(null, "Appointment Description");
		detailPanel.setBorder(detailBorder);
		detailArea = new JTextArea(20, 30);

		detailArea.setEditable(true);
		JScrollPane detailScroll = new JScrollPane(detailArea);
		detailPanel.add(detailScroll);

		pDes = new JSplitPane(JSplitPane.VERTICAL_SPLIT, titleAndTextPanel,
				detailPanel);

		top.add(pDes, BorderLayout.SOUTH);

		if (NewAppt != null) {
			detailArea.setText(NewAppt.getInfo());

		}

		JPanel panel2 = new JPanel();
		panel2.setLayout(new FlowLayout(FlowLayout.RIGHT));

		inviteBut = new JButton("Invite");
		inviteBut.addActionListener(this);
		panel2.add(inviteBut);

		rejectBut = new JButton("Reject");
		rejectBut.addActionListener(this);
		panel2.add(rejectBut);
		//rejectBut.show(true);		

		saveBut = new JButton("Save");
		saveBut.addActionListener(this);
		panel2.add(saveBut);

		CancelBut = new JButton("Cancel");
		CancelBut.addActionListener(this);
		panel2.add(CancelBut);

		contentPane.add("South", panel2);
		NewAppt = new Appt();

		if (this.getTitle().equals("Join Appointment Content Change") || this.getTitle().equals("Join Appointment Invitation")){
			inviteBut.show(false);
			rejectBut.show(true);
			CancelBut.setText("Consider Later");
			saveBut.setText("Accept");
		}
		if (this.getTitle().equals("Someone has responded to your Joint Appointment invitation") ){
			inviteBut.show(false);
			rejectBut.show(false);
			CancelBut.show(false);
			saveBut.setText("confirmed");
		}
		if (this.getTitle().equals("Join Appointment Invitation") || this.getTitle().equals("Someone has responded to your Joint Appointment invitation") || this.getTitle().equals("Join Appointment Content Change")){
			allDisableEdit();
		}
		pack();

	}

	AppScheduler(String title, CalGrid cal, int selectedApptId) {	// for modify function
		this.selectedApptId = selectedApptId;
		commonConstructor(title, cal);
	}

	AppScheduler(String title, CalGrid cal) {	// for new Appt function
		commonConstructor(title, cal);
	}

	public void actionPerformed(ActionEvent e) {

		// distinguish which button is clicked and continue with require function
		if (e.getSource() == CancelBut) {

			setVisible(false);
			dispose();
		} else if (e.getSource() == saveBut) {
			saveButtonResponse();
			setVisible(false);
			dispose();

		} else if (e.getSource() == rejectBut){
			if (JOptionPane.showConfirmDialog(this, "Reject this joint appointment?", "Confirmation", JOptionPane.YES_NO_OPTION) == 0){
				NewAppt.addReject(getCurrentUser());
				NewAppt.getAttendList().remove(getCurrentUser());
				NewAppt.getWaitingList().remove(getCurrentUser());
				this.setVisible(false);
				dispose();
			}
		} else if (e.getSource() == location) {	
			if (location.getSelectedItem().equals("More...")) {
				boolean invalidInput = false;
				while(invalidInput == false){
					JTextField newLocation = new JTextField();  

					Object[] message = {  
							"You can define your desired location here", newLocation,  

					};  
					JOptionPane optionPane = new JOptionPane(message );
					JDialog dialog = optionPane.createDialog("Enter all your information!");
					dialog.setAlwaysOnTop(this.isAlwaysOnTopSupported());
					dialog.setVisible(true);

						if(ValidString(newLocation.getText())){
							locations.set(locations.size() - 1 , newLocation.getText());
							locations.add("More...");

							location.removeAllItems();

							for (int cnt = 0; cnt < locations.size(); cnt++)
								location.addItem(locations.get(cnt));
							invalidInput = true;
						}else{

							JOptionPane j = new JOptionPane("INVALID INPUT for Location !" ,JOptionPane.ERROR_MESSAGE);
							JDialog d = j.createDialog("ERROR");
							d.setAlwaysOnTop(this.isAlwaysOnTopSupported());
							d.setVisible(true);

						}
					
					/*
					int option = JOptionPane.showConfirmDialog(null, message, "Enter all your information ", JOptionPane.OK_CANCEL_OPTION);  

					if (option == JOptionPane.OK_OPTION) {
						if(ValidString(newLocation.getText())){
							locations.set(locations.size() - 1 , newLocation.getText());
							locations.add("More...");

							location.removeAllItems();

							for (int cnt = 0; cnt < locations.size(); cnt++)
								location.addItem(locations.get(cnt));
							invalidInput = true;
						}else{
							JOptionPane.showMessageDialog(null,
									"INVALID INPUT for Location !",
									"ERROR",
									JOptionPane.WARNING_MESSAGE);
						}
					}else{	// choose the cancel option or "cross" , then the window disappear
						break;
					}*/
				}

			}
		}
		parent.getAppList().clear();
		parent.getAppList().setTodayAppt(parent.GetTodayAppt());
		parent.repaint();
	}

	private JPanel createPartOperaPane() {
		JPanel POperaPane = new JPanel();
		JPanel browsePane = new JPanel();
		JPanel controPane = new JPanel();

		POperaPane.setLayout(new BorderLayout());
		TitledBorder titledBorder1 = new TitledBorder(BorderFactory
				.createEtchedBorder(Color.white, new Color(178, 178, 178)),
				"Add Participant:");
		browsePane.setBorder(titledBorder1);

		POperaPane.add(controPane, BorderLayout.SOUTH);
		POperaPane.add(browsePane, BorderLayout.CENTER);
		POperaPane.setBorder(new BevelBorder(BevelBorder.LOWERED));
		return POperaPane;

	}

	private int[] getValidDate() {

		int[] date = new int[3];
		date[0] = Utility.getNumber(yearF.getText());
		date[1] = Utility.getNumber(monthF.getText());
		if (date[0] < 1980 || date[0] > 2100) {
			JOptionPane.showMessageDialog(this, "Please input proper year",
					"Input Error", JOptionPane.ERROR_MESSAGE);
			return null;
		}
		if (date[1] <= 0 || date[1] > 12) {
			JOptionPane.showMessageDialog(this, "Please input proper month",
					"Input Error", JOptionPane.ERROR_MESSAGE);
			return null;
		}

		date[2] = Utility.getNumber(dayF.getText());
		int monthDay = CalGrid.monthDays[date[1] - 1];
		if (date[1] == 2) {
			GregorianCalendar c = new GregorianCalendar();
			if (c.isLeapYear(date[0]))
				monthDay = 29;
		}
		if (date[2] <= 0 || date[2] > monthDay) {
			JOptionPane.showMessageDialog(this,
					"Please input proper month day", "Input Error",
					JOptionPane.ERROR_MESSAGE);
			return null;
		}
		return date;
	}

	private int getTime(JTextField h, JTextField min) {

		int hour = Utility.getNumber(h.getText());
		if (hour == -1)
			return -1;
		int minute = Utility.getNumber(min.getText());
		if (minute == -1)
			return -1;

		return (hour * 60 + minute);

	}

	private int[] getValidTimeInterval() {

		int[] result = new int[2];
		result[0] = getTime(sTimeH, sTimeM);
		result[1] = getTime(eTimeH, eTimeM);
		if ((result[0] % 15) != 0 || (result[1] % 15) != 0) {
			JOptionPane.showMessageDialog(this,
					"Minute Must be 0, 15, 30, or 45 !", "Input Error",
					JOptionPane.ERROR_MESSAGE);
			return null;
		}

		if (!sTimeM.getText().equals("0") && !sTimeM.getText().equals("15") && !sTimeM.getText().equals("30") && !sTimeM.getText().equals("45") 
				|| !eTimeM.getText().equals("0") && !eTimeM.getText().equals("15") && !eTimeM.getText().equals("30") && !eTimeM.getText().equals("45")){
			JOptionPane.showMessageDialog(this,
					"Minute Must be 0, 15, 30, or 45 !", "Input Error",
					JOptionPane.ERROR_MESSAGE);
			return null;
		}

		if (result[1] == -1 || result[0] == -1) {
			JOptionPane.showMessageDialog(this, "Please check time",
					"Input Error", JOptionPane.ERROR_MESSAGE);
			return null;
		}
		if (result[1] <= result[0]) {
			JOptionPane.showMessageDialog(this,
					"End time should be bigger than \nstart time",
					"Input Error", JOptionPane.ERROR_MESSAGE);
			return null;
		}
		if ((result[0] < AppList.OFFSET * 60)
				|| (result[1] > (AppList.OFFSET * 60 + AppList.ROWNUM * 2 * 15))) {
			JOptionPane.showMessageDialog(this, "Out of Appointment Range !",
					"Input Error", JOptionPane.ERROR_MESSAGE);
			return null;
		}

		return result;
	}

	private void saveButtonResponse() {

		int currCheck_sTimeH = 0;
		int currCheck_sTimeM = 0;
		int currCheck_eTimeH = 0;
		int currCheck_eTimeM = 0;
		int currCheck_rS = 0;	// the real start time
		int currCheck_rE = 0;	//	the real end time
		int newAppt_realStart = (Integer.parseInt(sTimeH.getText()))*60 + (Integer.parseInt(sTimeM.getText()));
		int newAppt_realEnd = (Integer.parseInt(eTimeH.getText()))*60 + (Integer.parseInt(eTimeM.getText()));

		boolean isTimeConflict = false;
		// Before saving all the data , Do ERROR Checking First!
		if(getValidDate() == null || getValidTimeInterval() == null){

		}else{

			String event = "src/hkust/cse/calendar/gui/event.txt";


			try{

				BufferedReader reader = new BufferedReader(new FileReader(event));
				String e;
				int count = 0;

				while ((e = reader.readLine()) != null) {	// read the file line by line
					count = 0;			
					StringTokenizer st = new StringTokenizer(e);

					while(st.hasMoreTokens()){
						if( count  == 0){	// year

							if(!(yearF.getText().equals(st.nextToken()))){
								System.out.println(yearF.getText());

								break;
							}

						}else if(count  == 1){	
							if(!(monthF.getText().equals(st.nextToken()))){
								break;
							}


						}else if(count == 2){	

							if(!(dayF.getText().equals(st.nextToken()))){
								break;
							}

						}else if(count == 3){

							currCheck_sTimeH = Integer.parseInt(st.nextToken());


						}else if(count == 4){
							currCheck_sTimeM = Integer.parseInt(st.nextToken());

						}else if(count == 5){
							currCheck_eTimeH = Integer.parseInt(st.nextToken());

						}else if(count == 6){
							currCheck_eTimeM = Integer.parseInt(st.nextToken());
							currCheck_rS = currCheck_sTimeH*60 + currCheck_sTimeM;
							currCheck_rE = currCheck_eTimeH*60 + currCheck_eTimeM;

							// last checking for time conflict
							if(newAppt_realStart >= currCheck_rS && newAppt_realStart <= currCheck_rE){
								isTimeConflict = true;
							}else if(newAppt_realEnd >= currCheck_rS && newAppt_realStart <= currCheck_rE){
								isTimeConflict = true;
							}

							break;

						}
						count++;
					}	// first while =>  jumping to next Token in the current line 
				}	

			}catch (Exception e){
				e.printStackTrace();

			}

			if(isTimeConflict == false){
				try{
					File f = new File(event);

					f.createNewFile();

					BufferedWriter output = new BufferedWriter(new FileWriter(f , true));

					output.write(yearF.getText() + " " + monthF.getText() + " " + 
							dayF.getText() + " " + sTimeH.getText() + " " + sTimeM.getText() +   
							" " + eTimeH.getText() + " " + eTimeM.getText() + " " + titleField.getText() + " " + 
							location.getSelectedItem() + " " 
							+ detailArea.getText() + "\n"); 

					output.close();

				}catch (Exception e){
					e.printStackTrace();

				}
			}else{
				/*
				JOptionPane.showMessageDialog(null,
						"Time Conflict with other appointments !",
						"ERROR",
						JOptionPane.WARNING_MESSAGE);*/
				JOptionPane optionPane = new JOptionPane("Time Conflict with another appointment!");
				JDialog dialog = optionPane.createDialog("ERROR!");
				dialog.setAlwaysOnTop(this.isAlwaysOnTopSupported());
				dialog.setVisible(true);
			}
		}
		// Fix Me!
		// Save the appointment to the hard disk
	}

	private Timestamp CreateTimeStamp(int[] date, int time) {
		Timestamp stamp = new Timestamp(0);
		stamp.setYear(date[0]);
		stamp.setMonth(date[1] - 1);
		stamp.setDate(date[2]);
		stamp.setHours(time / 60);
		stamp.setMinutes(time % 60);
		return stamp;
	}

	public void updateSetApp(Appt appt) {

		yearF.setText(appt.TimeSpan().StartTime().getYear() + 1900 + "");
		monthF.setText(appt.TimeSpan().StartTime().getMonth() + 1 + "");
		dayF.setText(appt.TimeSpan().StartTime().getDate() + "");
		sTimeH.setText(appt.TimeSpan().StartTime().getHours() + "");
		sTimeM.setText(appt.TimeSpan().StartTime().getMinutes() + "");
		eTimeH.setText(appt.TimeSpan().EndTime().getHours() + "");
		eTimeM.setText(appt.TimeSpan().EndTime().getMinutes() + "");
		titleField.setText(appt.getTitle() + "");
		location.setSelectedItem(appt.getLocation());
		detailArea.setText(appt.getInfo());
		// Fix Me!
	}

	public void componentHidden(ComponentEvent e) {

	}

	public void componentMoved(ComponentEvent e) {

	}

	public void componentResized(ComponentEvent e) {

		Dimension dm = pDes.getSize();
		double width = dm.width * 0.93;
		double height = dm.getHeight() * 0.6;
		detailPanel.setSize((int) width, (int) height);

	}

	public void componentShown(ComponentEvent e) {

	}

	public String getCurrentUser()		// get the id of the current user
	{
		return this.parent.mCurrUser.ID();
	}

	private void allDisableEdit(){
		yearF.setEditable(false);
		monthF.setEditable(false);
		dayF.setEditable(false);
		sTimeH.setEditable(false);
		sTimeM.setEditable(false);
		eTimeH.setEditable(false);
		eTimeM.setEditable(false);
		titleField.setEditable(false);
		detailArea.setEditable(false);
	}
	// This method checks whether a string is a valid user name or password, as they can contains only letters and numbers
	public static boolean ValidString(String s)
	{
		char[] sChar = s.toCharArray();
		if(s.length() == 0){
			return false;
		}
		for(int i = 0; i < sChar.length; i++)
		{
			int sInt = (int)sChar[i];
			if(sInt < 48 || sInt > 122)
				return false;
			if(sInt > 57 && sInt < 65)
				return false;
			if(sInt > 90 && sInt < 97)
				return false;
		}
		return true;
	}
}
